package com.learning.optimize.jdk.genericity;

import java.util.Arrays;
import java.util.List;

/**
 * ClassName: GenericityPECS
 * Description: PECS 原则
 * Date: 2018/7/25 13:26 【需求编号】
 *
 * @author Sam Sho
 * @version V1.0.0
 */
public class GenericityPesc {
    private List<Apple> apples = Arrays.asList(new Apple());
    private List<Fruit> fruits = Arrays.asList(new Fruit());

    /**
     * “Producer Extends” – 如果你需要一个只读List，用它来produce T，那么使用? extends T。
     */
    void test() {
        // 实例化对象，参数化类型为 Fruit
        Reader<Fruit> fruitReader = new Reader<>();
        Fruit f = fruitReader.readExact(fruits);

        // 传子类，编译就不过（参数化类型不考虑类型参数的继承关系）
        // Fruit a = fruitReader.readExact(apples);

        // 使用通配符，且限定下边界为 Fruit
        Fruit fruit = fruitReader.readCovariant(fruits);
        Fruit apple = fruitReader.readCovariant(apples);
    }

    /**
     * “Consumer Super” – 如果你需要一个只写List，用它来consume T，那么使用? super T。
     */
    void test2() {
        // 实例化对象，参数化类型为 Fruit
        Reader<Fruit> fruitReader = new Reader<>();

        // 使用通配符，且限定上边界为 Fruit
        fruitReader.writeWithWildcard(fruits, new Apple());
        fruitReader.writeWithWildcard(fruits, new Orange());
    }

    /**
     * JDK 源码中PECS原则使用： Collections
     *
     * @param dest
     * @param src
     * @param <T>
     */
    public static <T> void copy(List<? super T> dest, List<? extends T> src) {
        for (int i = 0; i < src.size(); i++) {
            dest.set(i, src.get(i));
        }
    }

    /**
     * @param <T>
     */
    class Reader<T> {
        T readExact(List<T> list) {
            return list.get(0);
        }

        /**
         * 接受的参数只要是满足Fruit的子类就行(包括Fruit自身)，这样子类和父类之间的关系也就关联上了
         * “Producer Extends” – 如果你需要一个只读List，用它来produce T，那么使用? extends T。
         *
         * @param list
         * @return
         */
        T readCovariant(List<? extends T> list) {
            return list.get(0);
        }

        /**
         * “Consumer Super” – 如果你需要一个只写List，用它来consume T，那么使用? super T。
         *
         * @param list
         * @param item
         */
        void writeWithWildcard(List<? super T> list, T item) {
            list.add(item);
        }
    }

}


class Fruit {
}

class Apple extends Fruit {
}

class Orange extends Fruit {
}
